//
// Created by harrybao on 2019/4/9.
//

#ifndef CMAKE_OBJECT_POOL_H
#define CMAKE_OBJECT_POOL_H

#include <new>
#include <cstdlib>
#include "abstract_pool.h"
#include "../../game/game.h"

class DefaultMemoryAllocator
{
public:
    static inline void *Allocate(size_t size)
    {
        return ::operator new(size, ::std::nothrow);
    }
    static inline void Deallocate(void *pointer, size_t size)
    {
        ::operator delete(pointer);
    }
};

//TODO
//https://www.codeproject.com/Articles/746630/O-Object-Pool-in-Cplusplus
template<typename T, class MemoryAllocator=DefaultMemoryAllocator>
class ObjectPool : AbstractPool
{
public:
    explicit ObjectPool()
    {
    }
    ~ObjectPool()
    {
        while (head_)
        {
            T* next = *((T **)head_);
            delete head_;
            head_ = next;
        }
    }
    static ObjectPool<T, MemoryAllocator>* Instance()
    {
        if (nullptr == singleton_)
        {
            singleton_ = new ObjectPool<T, MemoryAllocator>();
            size_t hash_code = typeid(T).hash_code();
            Game::Instance()->GetObjectPoolManager()->Register(hash_code, singleton_);
        }
        return singleton_;
    }
public:
    template<typename... Args>
    T* New(Args&&... args)
    {
        if (head_)
        {
            T* obj = head_;
            head_ = *((T **)head_);
            new (obj) T(std::forward<Args>(args)...);
            return obj;
        }
        else
        {
            return new T(std::forward<Args>(args)...);
        }
    }

    void Delete(T* obj)
    {
        obj->~T();
        *((T **)obj) = head_;
        head_ = obj;
    }

    void Recycle(void* data) override
    {
        Delete((T*)data);
    }

private:
    T* head_{ nullptr };
    static ObjectPool<T, MemoryAllocator>* singleton_;
};

template<typename T, class MemoryAllocator>
ObjectPool<T, MemoryAllocator>* ObjectPool<T, MemoryAllocator>::singleton_ = nullptr;

#endif //CMAKE_OBJECT_POOL_H
